﻿#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "Mine.h"
#include "IdDefine.h"

// 8个方向.
static int di[] = {-1, -1, -1, 0, 0, 1, 1, 1};
static int dj[] = {1, -1, 0, 1, -1, 1, -1, 0};

void Mine::GernalMineMap() {
    int col, row, _mines = 0;
    srand(time(NULL));
    memset(mineStatus, NO_MINE, sizeof(mineStatus));
    while(_mines < mines) {
        col = rand() % cols;
        row = rand() % rows;
        if(mineStatus[row][col] == NO_MINE) {
            mineStatus[row][col] = MINE;
            ++_mines;
        }
    }
    CalMineAround();
}

void Mine::CalMineAround() {
    int num;
    for(int i = 0; i < rows; ++i) {
        for(int j = 0; j < cols; ++j) {
            if(mineStatus[i][j] == MINE) continue;
            num = 0;
            for(int k = 0; k < 8; ++k) {
                if(IsOutOfBound(i+di[k], j+dj[k])) continue;
                if(mineStatus[i+di[k]][j+dj[k]] != MINE) continue;
                ++num;
            }
            mineStatus[i][j] = num;
        }
    }
}

void Mine::ClickRecursion(int row, int col) {
    // 如果该方格用户已标记或者已挖过,则不能继续挖
    if(userStatus[row][col] == MARK || userStatus[row][col] == DIG) {
        return;
    }
    userStatus[row][col] = DIG;
    PutArray(row, col);
    // 如果该方格周围8个方向存在雷或者该方格为雷,则不用继续挖.
    if(mineStatus[row][col] != 0) {
        if(mineStatus[row][col] == MINE) status = FAILED;
        else --blocks;
        return;
    }
    --blocks;
    for(int k = 0; k < 8; ++k) {
        if(IsOutOfBound(row+di[k], col+dj[k])) continue;
        ClickRecursion(row+di[k], col+dj[k]);
    }
}

bool Mine::Click(int row, int col, bool init) {
    if(IsGameFinish() || IsOutOfBound(row, col)
       || userStatus[row][col] == DIG || userStatus[row][col] == MARK) {
        return false;
    }
    if(init == true) lengthOfBlocksDig = currentPosOfBlocksDig = 0;
    if(mineStatus[row][col] == MINE) {
        // 如果第一步遇到雷且不是重新开始,则要重新调整雷区,否则遇雷失败.
        if(status == NO_START && !restart) {
            AdjustMine(row, col);
        } else {
            status = FAILED;
            userStatus[row][col] = DIG;
            PutArray(row, col);
            return true;
        }
    }
    status = STARTED;
    ClickRecursion(row, col);
    if(status == STARTED && blocks == 0) status = SUCESS;
    currentPosOfBlocksDig = 0;
    return lengthOfBlocksDig != 0;
}

bool Mine::MiddleClick(int row, int col) {
    if(IsGameFinish() || IsOutOfBound(row, col)
       || userStatus[row][col] != DIG) return false;
    int marks = 0;
    lengthOfBlocksDig = currentPosOfBlocksDig = 0;
    for(int k = 0; k < 8; ++k) {
        if(IsOutOfBound(row+di[k], col+dj[k])) continue;
        if(userStatus[row+di[k]][col+dj[k]] == MARK)
            ++marks;
    }
    if(mineStatus[row][col] != marks) return false;
    for(int k = 0; k < 8; ++k) {
        if(IsOutOfBound(row+di[k], col+dj[k])
           || userStatus[row+di[k]][col+dj[k]] == MARK
           || userStatus[row+di[k]][col+dj[k]] == DIG) continue;
        Click(row+di[k], col+dj[k], false);
    }
    return lengthOfBlocksDig != 0;
}

void Mine::AdjustMine(int row, int col) {
    if(mineStatus[row][col] != MINE) return;
    int _row = row, _col = col, trow, tcol;
    srand(time(NULL));
    while(mineStatus[_row][_col] == MINE) {
        _row = rand() % rows;
        _col = rand() % cols;
    }
    // 原来方格变为无雷,其周围8个方格的含雷数-1.
    mineStatus[row][col] = NO_MINE;
    for(int k = 0; k < 8; ++k) {
        trow = row + di[k];
        tcol = col + dj[k];
        if(IsOutOfBound(trow, tcol)) continue;
        if(mineStatus[trow][tcol] == MINE) {
            ++mineStatus[row][col];
            continue;
        }
        mineStatus[trow][tcol] -= 1;
    }
    // 新的方格变为有雷，其周围8个方格的含雷数+1.
    mineStatus[_row][_col] = MINE;
    for(int k = 0; k < 8; ++k) {
        trow = _row + di[k];
        tcol = _col + dj[k];
        if(IsOutOfBound(trow, tcol) || mineStatus[trow][tcol] == MINE) continue;
        mineStatus[trow][tcol] += 1;
    }
}

void Mine::NewGame(bool _restart) {
    this->restart = _restart;
    // 重置游戏状态为未开始.
    this->status = NO_START;
    // 重置用户操作状态.
    memset(this->userStatus, NO_ACTION, sizeof(this->userStatus));
    // 重置已标记的雷数为0.
    this->markMines = 0;
    // 重新设定需要打开的方块数
    this->blocks = this->rows * this->cols - this->mines;
    // 如果不是重新上一幅地图,则要重新生成地图.
    if(this->restart != true) {
        GernalMineMap();
    }
}
